﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using OpenCodeProcessorFramework.Languages.Ruby;
using Paraiba.Collections.Generic;
using Paraiba.Xml.Linq;

namespace Metricser
{
	class Cyclomatic
	{
		public static Dictionary<string, int> Measure(string filePath)
		{
			var counts = new Dictionary<string, int>();
			var ast = RubyAstGenerator.Instance.GenerateFromFile(filePath);
			Initialize(ast, counts);
			CountBranches(RubyElements.WhileUntil(ast), counts);
			CountBranches(RubyElements.If(ast), counts);
			CountBranches(RubyElements.Iterator(ast), counts);
			CountBranches(RubyElements.Case(ast).SelectMany(RubyElements.When), counts);
			return counts;
		}

		private static void Initialize(XElement ast, IDictionary<string, int> counts)
		{
			var outers = RubyElements.Class(ast)
				.Concat(RubyElements.SingularClass(ast))
				.Concat(RubyElements.Module(ast))
				.Concat(RubyElements.Method(ast));
			foreach(var e in outers) {
				var outerStr = GetOutersString(e);
				counts[outerStr] = 0;
			}
		}

		private static void CountBranches(IEnumerable<XElement> targets, IDictionary<string, int> counts)
		{
			foreach(var e in targets) {
				var outerStr = GetOutersString(e);
				counts.Increment(outerStr);
			}
		}

		private static string GetOutersString(XElement target)
		{
			var result = "";
			foreach(var e in target.ParentsAndSelf()) {
				string modName;
				switch(e.Name.LocalName) {
				case "class":
					modName = "class";
					break;
				case "module":
					modName = "module";
					break;
				case "sclass":
					modName = "sclass";
					break;
				case "defn":
					modName = "method";
					break;
				default:
					continue;
				}
				result = modName + " " + e.Descendants("Symbol").First().Value + "::" + result;
			}
			if (string.IsNullOrEmpty(result)) {
				result = "::";
			}
			return result;
		}
	}
}
